import JSZip from "jszip";
import { DropboxUtils } from "./dropbox";
import { ProjectService } from "@/service/projects-api";
import { OptionsApi } from "@/service/options-api";
import { AUTH_DIRECT_TIMEOUT, BACKUP_CONFIG_NAME, BACKUP_PROJECTS_NAME } from "./conf";
import { CacheUtils } from "../cache";
import { MessageBox } from "../messagebox";
import { ComUtils } from "../Com";
import { OneDriveUtils } from "./oneDrive";
import { GoogleDriveUtils } from "./googleDrive";
import { AliyunDriveUtils } from "./aliyun";

const AUTO_BACKUP_TIME_KEY = 'sg-auto-lastBackupTime'
export namespace Backups {
    export const state = {
        type: 'aliyundrive' as BackupsType,
        lastBackupTime: 0
    }
    let driver: BackupDriver | undefined
    export async function init() {
        const conf = await OptionsApi.getConfig()
        if (conf) {
            state.type = conf.backups.type
        }

        switch (state.type) {
            case 'dropbox':
                driver = new DropboxUtils()
                break;
            case 'onedrive':
                driver = new OneDriveUtils()
                break;
            case 'aliyundrive':
                driver = new AliyunDriveUtils()
                break;
            default:
                driver = new GoogleDriveUtils()
                break;
        }
        state.lastBackupTime = Number(await CacheUtils.get(AUTO_BACKUP_TIME_KEY) || 0) || 0
        await driver?.init()
    }

    export async function cancelAuth() {
        if (!driver) {
            throw new Error('driver is undefined')
        }
        await driver.cancelAuth()
    }

    export async function autoBackup() {
        try {
            const conf = await OptionsApi.getConfig()
            if (!conf) {
                return
            }
            if (!conf.backups.auto || !driver) {
                return
            }
            if (!await isAuth()) {
                MessageBox.info(ComUtils.tr('backup_need_auth') + ' ' + driver.name)
                return
            }
            const now = Date.now()
            const autoTime = conf?.backups.autoTime || AUTH_DIRECT_TIMEOUT
            if (now - state.lastBackupTime < autoTime) {
                return
            }
            console.log('auto backup', state.lastBackupTime)
            state.lastBackupTime = now
            await save()
        } catch (error) {
            console.log(error)
            MessageBox.error(ComUtils.tr('backup_fail'))
        }
    }

    export async function isAuth() {
        if (!driver) {
            throw new Error('driver is undefined')
        }
        return await driver.isAuth()
    }

    export function auth() {
        return new Promise((resolve, reject) => {
            if (!driver) {
                reject('driver is undefined')
                return
            }
            let timer: any
            let win: Window | null
            const handler = async (e: MessageEvent) => {
                const { type, hash, token, expires } = e.data
                if (type == 'backupAuth' && hash && driver) {
                    clearTimeout(timer)
                    window.removeEventListener('message', handler)
                    win?.close()
                    const code = await driver.getCode(hash)
                    if (code) {
                        const token = await driver.getTokenWithCode(code, hash)
                        console.log(token)
                        resolve(token)
                        return
                    }
                    const access_token = await driver.getToken(hash)
                    if (access_token) {
                        const expires_in = driver.getTokenExpires(hash)
                        CacheUtils.set(driver.getCacheKey(), access_token, Date.now() + 1000 * Number(expires_in || 3600))
                        resolve(access_token)
                    } else {
                        console.log(hash)
                        reject('auth error')
                    }
                } else if (type == 'backupAuthOk' && driver && token) {
                    clearTimeout(timer)
                    window.removeEventListener('message', handler)
                    win?.close()
                    CacheUtils.set(driver.getCacheKey(), token, Date.now() + 1000 * Number(expires || 3600))
                    resolve(token)
                }
            }
            window.addEventListener('message', handler)
            timer = setTimeout(() => {
                window.removeEventListener('message', handler)
                reject('auth timeout')
            }, 1000 * 60)

            driver.auth().then(url => {
                if (!url.trim()) {
                    return
                }
                win = window.open(url, '_blank')
                if (!win) {
                    reject('auth error')
                    return
                }
                win.onload = () => {
                    win?.postMessage({ type: 'backupAuth' }, '*')
                }
            }).catch(reject)
        })
    }

    export async function save() {
        if (!driver) {
            throw new Error('driver is undefined')
        }

        const zip = new JSZip();
        //项目
        const projects = await ProjectService.list()
        const projectsText = JSON.stringify(projects)
        const projectsBlob = new Blob([projectsText], { type: 'text/json' })
        zip.file(BACKUP_PROJECTS_NAME, projectsBlob)
        //配置
        const conf = (await OptionsApi.getConfig()) || {}
        const confText = JSON.stringify(conf)
        const confBlob = new Blob([confText], { type: 'text/json' })
        zip.file(BACKUP_CONFIG_NAME, confBlob)
        const blob = await zip.generateAsync({ type: "blob" })
        await driver?.save(blob)

        CacheUtils.set(AUTO_BACKUP_TIME_KEY, String(Date.now()))
    }

    export async function packBackup() {
        const zip = new JSZip();
        //项目
        const projects = await ProjectService.list()
        const projectsText = JSON.stringify(projects)
        const projectsBlob = new Blob([projectsText], { type: 'text/json' })
        zip.file(BACKUP_PROJECTS_NAME, projectsBlob)
        //配置
        const conf = (await OptionsApi.getConfig()) || {}
        const confText = JSON.stringify(conf)
        const confBlob = new Blob([confText], { type: 'text/json' })
        zip.file(BACKUP_CONFIG_NAME, confBlob)
        const blob = await zip.generateAsync({ type: "blob" })
        return blob
    }

    export async function unpackBackup(file: File) {
        const zip = new JSZip();
        await zip.loadAsync(file)
        let projects: ProjectItem[] = []
        let conf = {} as OptionsInfo
        for (const key in zip.files) {
            const file = zip.files[key]
            if (key == BACKUP_CONFIG_NAME) {
                conf = JSON.parse(await file.async('string'))
            } else if (key == BACKUP_PROJECTS_NAME) {
                projects = JSON.parse(await file.async('string'))
            }
        }
        return { projects, conf }
    }

    export async function load() {
        if (!driver) {
            throw new Error('driver is undefined')
        }
        const info = await driver?.load()
        if (!info) {
            return
        }
        const zip = new JSZip();
        await zip.loadAsync(info.file)
        let projects: ProjectItem[] = []
        let conf = {} as OptionsInfo
        for (const key in zip.files) {
            const file = zip.files[key]
            if (key == BACKUP_CONFIG_NAME) {
                conf = JSON.parse(await file.async('string'))
            } else if (key == BACKUP_PROJECTS_NAME) {
                projects = JSON.parse(await file.async('string'))
            }
        }
        await ProjectService.saveAll(projects)
        await OptionsApi.save(conf)
        return { projects, conf }
    }
}